package com.enation.app.javashop.core.promotion.seckill.service.impl;

import com.enation.app.javashop.core.base.CachePrefix;
import com.enation.app.javashop.core.client.goods.GoodsClient;
import com.enation.app.javashop.core.goods.model.vo.CacheGoods;
import com.enation.app.javashop.core.promotion.PromotionErrorCode;
import com.enation.app.javashop.core.promotion.seckill.model.dos.SeckillApplyDO;
import com.enation.app.javashop.core.promotion.seckill.model.dos.SeckillDO;
import com.enation.app.javashop.core.promotion.seckill.model.dos.SeckillRangeDO;
import com.enation.app.javashop.core.promotion.seckill.model.dto.SeckillQueryParam;
import com.enation.app.javashop.core.promotion.seckill.model.enums.SeckillGoodsApplyStatusEnum;
import com.enation.app.javashop.core.promotion.seckill.model.enums.SeckillStatusEnum;
import com.enation.app.javashop.core.promotion.seckill.model.enums.SeckillTypeEnum;
import com.enation.app.javashop.core.promotion.seckill.model.vo.SeckillGoodsVO;
import com.enation.app.javashop.core.promotion.seckill.model.vo.SeckillVO;
import com.enation.app.javashop.core.promotion.seckill.model.vo.TimeLineGoods;
import com.enation.app.javashop.core.promotion.seckill.service.SeckillGoodsManager;
import com.enation.app.javashop.core.promotion.seckill.service.SeckillManager;
import com.enation.app.javashop.core.promotion.seckill.service.SeckillPromotionManager;
import com.enation.app.javashop.core.promotion.seckill.service.SeckillRangeManager;
import com.enation.app.javashop.core.promotion.tool.model.enums.PromotionTypeEnum;
import com.enation.app.javashop.core.promotion.tool.service.impl.AbstractPromotionRuleManagerImpl;
import com.enation.app.javashop.core.promotion.tool.support.PromotionCacheKeys;
import com.enation.app.javashop.core.shop.service.ShopManager;
import com.enation.app.javashop.core.trade.cart.model.vo.CartSkuOriginVo;
import com.enation.app.javashop.core.trade.cart.service.CartOriginDataManager;
import com.enation.app.javashop.framework.context.UserContext;
import com.enation.app.javashop.framework.database.DaoSupport;
import com.enation.app.javashop.framework.database.Page;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.security.model.Buyer;
import com.enation.app.javashop.framework.util.DateUtil;
import com.enation.app.javashop.framework.util.StringUtil;
import com.google.common.collect.Maps;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 限时抢购入库业务类
 *
 * @author Snow
 * @version v7.0.0
 * @since v7.0.0
 * 2018-03-21 10:32:36
 */
@Service
public class SeckillManagerImpl extends AbstractPromotionRuleManagerImpl implements SeckillManager {

    @Autowired
    @Qualifier("tradeDaoSupport")
    private DaoSupport daoSupport;

    @Autowired
    private SeckillRangeManager seckillRangeManager;

    @Autowired
    private SeckillGoodsManager seckillGoodsManager;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private ShopManager shopManager;

    @Autowired
    private SeckillPromotionManager seckillPromotionManager;
    @Autowired
    private CartOriginDataManager cartOriginDataManager;

    @Override
    public Page list(int page, int pageSize, SeckillQueryParam seckillQueryParam) {

        List params = new ArrayList();

        String sql = "select * from es_seckill where 1=1 ";
        if (!StringUtil.isEmpty(seckillQueryParam.getKeywords())) {
            sql += " and seckill_name like ?";
            params.add("%" + seckillQueryParam.getKeywords() + "%");
        }
        if (seckillQueryParam.getSellerId()!=null) {
            sql += " and seller_ids = ? and seckill_type='"+ SeckillTypeEnum.SELLLER.name()+"'";
            params.add(seckillQueryParam.getSellerId());
        }else{
            sql += " and seckill_type='"+ SeckillTypeEnum.PLATFORM.name()+"'";
        }
        sql += " order by start_day desc";
        Page webPage = this.daoSupport.queryForPage(sql, page, pageSize, SeckillDO.class, params.toArray());

        List<SeckillVO> seckillVOList = new ArrayList<>();
        List<SeckillDO> seckillDOList = webPage.getData();
        for (SeckillDO seckillDO : seckillDOList) {

            List<Integer> rangeList = new ArrayList<>();
            List<SeckillRangeDO> rangeDOList = this.seckillRangeManager.getList(seckillDO.getSeckillId());
            for (SeckillRangeDO rangeDO : rangeDOList) {
                rangeList.add(rangeDO.getRangeTime());
            }

            SeckillVO seckillVO = new SeckillVO();
            BeanUtils.copyProperties(seckillDO, seckillVO);

            if (seckillVO.getSeckillStatus() != null) {
                SeckillStatusEnum statusEnum = SeckillStatusEnum.valueOf(seckillVO.getSeckillStatus());
                //如果状态是已发布状态，则判断该活动是否已开始或者已结束
                seckillVO.setSeckillStatusText(statusEnum.description());
                if (SeckillStatusEnum.RELEASE.equals(statusEnum)) {

                    //活动开始时间
                    long startDay = seckillDO.getStartDay();

                    if (DateUtil.startOfTodDay() <= startDay && DateUtil.endOfTodDay() > startDay) {
                        seckillVO.setSeckillStatusText("已开启");
                    } else if (startDay < DateUtil.endOfTodDay()) {
                        seckillVO.setSeckillStatusText("已关闭");
                    }

                }
            }

            seckillVO.setRangeList(rangeList);
            seckillVOList.add(seckillVO);
        }
        webPage.setData(seckillVOList);
        return webPage;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ServiceException.class, RuntimeException.class})
    public SeckillVO add(SeckillVO seckill) {

        String sql = "select * from es_seckill where seckill_name = ? ";
        List list = this.daoSupport.queryForList(sql, seckill.getSeckillName());
        if (list.size() > 0) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "活动名称重复");
        }

        String date = DateUtil.toString(seckill.getStartDay(), "yyyy-MM-dd");
        long startTime = DateUtil.getDateline(date + " 00:00:00", "yyyy-MM-dd HH:mm:ss");
        long endTime = DateUtil.getDateline(date + " 23:59:59", "yyyy-MM-dd HH:mm:ss");
        this.verifyTime(startTime, endTime, PromotionTypeEnum.SECKILL, null);

        SeckillDO seckillDO = new SeckillDO();
        BeanUtils.copyProperties(seckill, seckillDO);
        this.daoSupport.insert(seckillDO);

        Integer id = this.daoSupport.getLastId("es_seckill");
        seckill.setSeckillId(id);

        this.seckillRangeManager.addList(seckill.getRangeList(), id);
        return seckill;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ServiceException.class})
    public SeckillVO edit(SeckillVO seckill, Integer id) {

        String sql = "select * from es_seckill where seckill_name = ? and seckill_id != ? ";
        List list = this.daoSupport.queryForList(sql, seckill.getSeckillName(), id);
        if (list.size() > 0) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "活动名称重复");
        }

        String date = DateUtil.toString(seckill.getStartDay(), "yyyy-MM-dd");
        long startTime = DateUtil.getDateline(date + " 00:00:00", "yyyy-MM-dd HH:mm:ss");
        long endTime = DateUtil.getDateline(date + " 23:59:59", "yyyy-MM-dd HH:mm:ss");
        this.verifyTime(startTime, endTime, PromotionTypeEnum.SECKILL, id);

        SeckillDO seckillDO = new SeckillDO();
        BeanUtils.copyProperties(seckill, seckillDO);

        this.daoSupport.update(seckillDO, id);

        this.seckillRangeManager.addList(seckill.getRangeList(), id);
        return seckill;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ServiceException.class})
    public void delete(Integer id) {

        SeckillVO seckill = this.getModel(id);
        if (seckill == null) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "活动不存在");
        }
        //只要活动未开启，就可以删除活动
        String statusEnum = seckill.getSeckillStatus();
        boolean flag = false;
        if (SeckillStatusEnum.RELEASE.name().equals(statusEnum)) {
            //活动开始时间
            long startDay = seckill.getStartDay();

            if (DateUtil.startOfTodDay() > startDay) {
                flag = true;
            }
        }
        //编辑中可以删除
        if (SeckillStatusEnum.EDITING.name().equals(statusEnum)) {
            flag = true;
        }

        if (flag) {
            this.daoSupport.delete(SeckillDO.class, id);
            //将参与该活动的商品全部删除

        } else {
            throw new ServiceException(PromotionErrorCode.E400.code(), "该活动不是能删除的状态");
        }

    }

    @Override
    public SeckillVO getModel(Integer id) {

        SeckillDO seckillDO = this.daoSupport.queryForObject(SeckillDO.class, id);
        if (seckillDO == null) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "活动不存在");
        }
        SeckillVO seckillVO = new SeckillVO();
        BeanUtils.copyProperties(seckillDO, seckillVO);

        List<Integer> rangeList = new ArrayList<>();
        List<SeckillRangeDO> rangeDOList = this.seckillRangeManager.getList(id);
        for (SeckillRangeDO rangeDO : rangeDOList) {
            rangeList.add(rangeDO.getRangeTime());
        }
        seckillVO.setRangeList(rangeList);
        return seckillVO;
    }

    @Override
    public SeckillVO getSeckillDetail(Integer seckillId) {
        SeckillVO seckill = this.getModel(seckillId);

        Map<Integer, List<SeckillGoodsVO>> seckillGoodsGroup = seckillGoodsManager.getSeckillGoodsList(seckill.getStartDay(),seckillId);
        List<Integer> rangeList = seckill.getRangeList();
        List<TimeLineGoods> timeLineGoodsList = new ArrayList<>();
        for (Integer rangeTime : rangeList) {
            TimeLineGoods timeLineGoods = new TimeLineGoods();
            // 时间点
            timeLineGoods.setTimeLine(rangeTime);
            // 商品集合
            List<SeckillGoodsVO> seckillGoodsVOList = seckillGoodsGroup.get(rangeTime);
            timeLineGoods.setSeckillGoodsList(seckillGoodsVOList);

            timeLineGoodsList.add(timeLineGoods);
        }
        seckill.setTimeLineGoodsList(timeLineGoodsList);
        return seckill;
    }

    @Override
    public SeckillGoodsVO getSeckillGoods(Integer skuId) {

        Map<Integer, List<SeckillGoodsVO>> map = this.seckillGoodsManager.getSeckillGoodsList(null,null);
        SeckillGoodsVO goodsVO = null;
        for (Map.Entry<Integer, List<SeckillGoodsVO>> entry : map.entrySet()) {
            List<SeckillGoodsVO> list = entry.getValue();

            for (SeckillGoodsVO seckillGoods : list) {
                if (seckillGoods.getSkuId().equals(skuId)) {
                    goodsVO = new SeckillGoodsVO(seckillGoods, entry.getKey());
                }
            }
        }
        return goodsVO;
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ServiceException.class, RuntimeException.class})
    public void reviewApply(Integer applyId, String status, String failReason) {

        if (failReason.length() > 500){
            throw new ServiceException(PromotionErrorCode.E400.code(), "错误原因长度不能超过500个字符");
        }
        String sql = "select *  from es_seckill_apply where apply_id = ? and status = ? ";
        SeckillApplyDO apply = this.daoSupport.queryForObject(sql, SeckillApplyDO.class, applyId, SeckillGoodsApplyStatusEnum.APPLY.name());
        //申请不存在
        if (apply == null) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "不是可以审核的状态");
        }
        //状态值不正确
        SeckillGoodsApplyStatusEnum applyStatusEnum = SeckillGoodsApplyStatusEnum.valueOf(status);

        //驳回，原因必填
        if (applyStatusEnum.equals(SeckillGoodsApplyStatusEnum.FAIL) && StringUtil.isEmpty(failReason)) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "驳回原因必填");
        }
        apply.setStatus(applyStatusEnum.name());
        apply.setFailReason(failReason);

        Map where = new HashMap(16);
        where.put("apply_id", applyId);
        this.daoSupport.update("es_seckill_apply", apply, where);
        //查询商品
        CacheGoods goods = goodsClient.getFromCache(apply.getGoodsId());
        //将审核通过的商品，存储到活动商品表和缓存中
        if (applyStatusEnum.equals(SeckillGoodsApplyStatusEnum.PASS)) {
            // 设置es_promotion_goods 添加活动
            apply.setSellerId(goods.getSellerId());
            seckillPromotionManager.addPromotionGoods(apply);
        }

    }

    @Override
    public void sellerApply(Integer sellerId, Integer seckillId) {
        SeckillDO seckillDO = this.getModel(seckillId);
        String sellerIds;
        if (!StringUtil.isEmpty(seckillDO.getSellerIds())) {
            sellerIds = seckillDO.getSellerIds() + sellerId + ",";
        } else {
            sellerIds = sellerId + ",";
        }
        String sql = "update es_seckill set seller_ids = ? where seckill_id = ? ";
        this.daoSupport.execute(sql, sellerIds, seckillId);
    }

    @Override
    public void close(Integer id) {

        SeckillVO seckill = this.getModel(id);
        if (seckill == null) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "活动不存在");
        }

        String statusEnum = seckill.getSeckillStatus();
        if (SeckillStatusEnum.RELEASE.name().equals(statusEnum)) {

            //活动开始时间
            long startDay = seckill.getStartDay();

            //已开启状态
            if (DateUtil.startOfTodDay() < startDay && DateUtil.endOfTodDay() > startDay) {
                //此时可以暂停

            }
        }

    }

    @Override
    public SeckillVO getSeckillDetailByCity(String city) {
        long startDay = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).toInstant(ZoneOffset.of("+8")).toEpochMilli() / 1000;
        long currTime = DateUtil.getDateline();
        long distanceEndTime =LocalDateTime.of(LocalDate.now(), LocalTime.MAX).toInstant(ZoneOffset.of("+8")).toEpochMilli() / 1000 -currTime;

        SeckillDO seckillDO = getSeckillByCity(city, startDay);

        if(seckillDO==null){
             return null;
        }

        SeckillVO seckillVO = this.getSeckillDetail(seckillDO.getSeckillId());
        // 去除空商品时间点
        List<TimeLineGoods> timeLineGoodsList = seckillVO.getTimeLineGoodsList();
        List<TimeLineGoods> resultList=new ArrayList<>();

        Map<Integer, Integer> cartNumMap = cartOriginDataManager.getUserCart();

        for (TimeLineGoods timeLineGoods : timeLineGoodsList) {
            if (CollectionUtils.isEmpty(timeLineGoods.getSeckillGoodsList())) {
                continue;
            }
            Integer isStart=1;
            long startTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(timeLineGoods.getTimeLine(), 0)).toInstant(ZoneOffset.of("+8")).toEpochMilli() / 1000;

            Long distanceStartTime=null;
            if (currTime < startTime) {
                distanceStartTime = startTime - currTime;
                timeLineGoods.setDistanceStartTime(distanceStartTime);
                isStart = 0;
            }
            List<SeckillGoodsVO> seckillGoodsList = timeLineGoods.getSeckillGoodsList();
            for (SeckillGoodsVO seckillGoodsVO : seckillGoodsList) {
                // 购物车社区团商品数量
                Integer cartNum = cartNumMap.get(seckillGoodsVO.getSkuId());
                seckillGoodsVO.setCartNum(cartNum == null ? 0 : cartNum);
                seckillGoodsVO.setIsStart(isStart);
                seckillGoodsVO.setDistanceStartTime(distanceStartTime);
                // 秒杀商品是否可售
                seckillGoodsVO.setSalesEnable(seckillGoodsVO.getRemainQuantity() > 0);
                timeLineGoods.setDistanceEndTime(distanceEndTime);
            }
            resultList.add(timeLineGoods);
        }
        seckillVO.setTimeLineGoodsList(resultList);
        return seckillVO;
    }

    @Override
    public  SeckillDO getSeckillByCity(String city, long startDay) {
        Integer shopId = shopManager.getShopByCity(city);
        String sql = "select * from es_seckill where start_day = ? and seller_ids= ? ";
        return this.daoSupport.queryForObject(sql, SeckillDO.class,startDay, shopId);
    }



    /**
     * 获取限时抢购key
     * @param dateline
     * @return
     */
    private String getRedisKey(long dateline) {
        return PromotionCacheKeys.getSeckillKey(DateUtil.toString(dateline, "yyyyMMdd"));
    }

}
